package com.scs.application.modules.wm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.SerialKeys;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.flow.FlowContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.core.utils.UtilCommon;
import com.scs.application.core.utils.UtilNum;
import com.scs.application.modules.base.entity.Dept;
import com.scs.application.modules.base.entity.Location;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.entity.Warehouse;
import com.scs.application.modules.base.mapper.MatrMapper;
import com.scs.application.modules.base.service.DeptService;
import com.scs.application.modules.base.service.LocationLimitMatrService;
import com.scs.application.modules.base.service.LocationService;
import com.scs.application.modules.base.service.WarehouseService;
import com.scs.application.modules.base.util.CommonService;
import com.scs.application.modules.base.util.MatrPassUtil;
import com.scs.application.modules.cfg.utils.ParameterUtils;
import com.scs.application.modules.dept.entity.ApplyAuto;
import com.scs.application.modules.dept.entity.ApplyAutoHistory;
import com.scs.application.modules.dept.entity.Demand;
import com.scs.application.modules.dept.service.*;
import com.scs.application.modules.dept.service.impl.ApplyItemServiceImpl;
import com.scs.application.modules.dept.util.UtilBudget;
import com.scs.application.modules.purchase.entity.Order;
import com.scs.application.modules.purchase.enums.PurchaseType;
import com.scs.application.modules.purchase.service.OrderService;
import com.scs.application.modules.sys.entity.FlowButton;
import com.scs.application.modules.wm.dto.AdjustLocationDTO;
import com.scs.application.modules.wm.entity.*;
import com.scs.application.modules.wm.enums.LockBusType;
import com.scs.application.modules.wm.enums.WorkOrderType;
import com.scs.application.modules.wm.mapper.*;
import com.scs.application.modules.wm.service.*;
import com.scs.application.modules.wm.vo.TransferVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 */
@Slf4j
@Service
@AllArgsConstructor
public class TransferServiceImpl extends BaseServiceImpl<TransferMapper, Transfer> implements TransferService {

    private WorkOrderService workOrderService;
    private OutOrderService outOrderService;
    private InOrderService inOrderService;
    private TransferItemMapper transferItemMapper;
    private WorkOrderItemMapper workOrderItemMapper;
    private OrderService orderService;
    private DeptService deptService;
    private StockService stockService;
    private ApplyAutoService applyAutoService;
    private ApplyAutoHistoryService applyAutoHistoryService;
    private StockLockService stockLockService;
    private TransferItemService transferItemService;
    private WarehouseService warehouseService;
    private LocationService locationService;
    private TransferDemandMapper transferDemandMapper;
    private StockCheckMapper stockCheckMapper;
    private MatrPassUtil matrPassUtil;
    private LocationLimitMatrService locationLimitMatrService;
    private UseService useService;
    private MatrMapper matrMapper;
    private DemandService demandService;
    private StockZeroService stockZeroService;
    private OprtPrepareService oprtPrepareService;
    private OprtPrepareItemService oprtPrepareItemService;
    private OprtApplyService oprtApplyService;

    @Override
    public boolean saveOrUpdate(Transfer transfer) {
        if (StringUtils.isBlank(transfer.getBusKey())) {
            transfer.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY));
        }
        if (StringUtils.isBlank(transfer.getType())) {
            transfer.setType(WorkOrderType.OUT_SHIFT);
        }

        if (GlobalConsts.SettlementType.outSettlement.equalsIgnoreCase(transfer.getSettlementType()) && GlobalConsts.TransferType.distOut.equalsIgnoreCase(transfer.getType())) { //出库结算的，不能是中心仓库
            Warehouse warehouse = warehouseService.getById(transfer.getToWarehouseId());
            if (warehouse != null && warehouse.getFlagCenter()) {
                throw new BusinessException("该调拨方向不支持出库结算类型");
            }
        }

        Dept deptTo = deptService.getDeptByWarehouseId(transfer.getToWarehouseId());
        if (deptTo != null) {
            transfer.setDeptIdTo(deptTo.getId()).setDeptNameTo(deptTo.getName());
        } else if (GlobalConsts.SettlementType.outSettlement.equalsIgnoreCase(transfer.getSettlementType())) {
            throw new BusinessException("选择的调入仓库没有对应的二级科室，请先设置后再调拨！");
        }

        if (StringUtils.isBlank(transfer.getFromWarehouseId()) && StringUtils.isNotBlank(transfer.getDeptIdFrom())) {
           Dept deptFrom = deptService.getById(transfer.getDeptIdFrom());
            if (deptFrom != null) {
                transfer.setFromWarehouseId(deptFrom.getWarehouseId()).setFromWarehouseName(deptFrom.getWarehouseName());
            }
        } else {
            Dept deptFrom = deptService.getDeptByWarehouseId(transfer.getFromWarehouseId());
            if (deptFrom != null) {
                transfer.setDeptIdFrom(deptFrom.getId()).setDeptNameFrom(deptFrom.getName());
            }
        }

        if (StringUtils.isBlank(transfer.getFlowStatus())) { //根据来源来觉得流程状态
            if (GlobalConsts.DistTransferSourceType.MANUAL.equals(transfer.getSourceType())) { //手动的，直接从拣货开始
                transfer.setFlowStatus(GlobalConsts.FLOW_PICKING_VALUE);
            } else {
                transfer.setFlowStatus(GlobalConsts.FLOW_START_VALUE);
            }
            transfer.setFlowModified(LocalDateTime.now())
                    .setFlowModifier(UserUtils.currentUser().getName());
        }
        return super.saveOrUpdate(transfer);
    }

    @Override
    public boolean removeCascadeByIds(Collection<? extends Serializable> idList) {
        //删除所有调拨需求记录
        List<Transfer> items = super.list(new QueryWrapper<Transfer>()
                .in("id", idList));
        Transfer transfer = items.get(0);
        removeTransferDemand(transfer);

        List<TransferItem> transferItems = transferItemService.list(Wrappers.<TransferItem>query().in("transfer_id", idList));
        if (transferItems != null && transferItems.size() > 0) {
            transferItemService.removeCascadeByIds(transferItems.stream().map(TransferItem::getId).collect(Collectors.toList()));
        }

        List<ApplyAuto> applyAutoList = applyAutoService.list(new QueryWrapper<ApplyAuto>().eq("ref_id", transfer.getId()));
        if (applyAutoList != null && applyAutoList.size() > 0) {
            //请领需求产生的
            List<ApplyAuto> applyAutoListTmp = applyAutoList.stream().filter(tmp -> StringUtils.isNotBlank(tmp.getRefApplyItemId())).collect(Collectors.toList());
            if (applyAutoListTmp != null && applyAutoListTmp.size() > 0) {
                applyAutoListTmp.stream().forEach(applyAuto -> {
                    double count = transferItemMapper.selectCount(new QueryWrapper<TransferItem>()
                            .eq("transfer_id", applyAuto.getRefId())
                            .eq("matr_id", applyAuto.getMatrId()));
                    applyAutoService.update(Wrappers.<ApplyAuto>update().set("picked_sku_qty", count)
                            .setSql(" ref_id=null").setSql("gmt_response = null").eq("id", applyAuto.getId()))
                    ;
                });
            }


            //由自动请领产生的调拨单
            List<String> autoApplyIds = applyAutoList.stream().filter(tmp -> StringUtils.isBlank(tmp.getRefApplyItemId())).map(ApplyAuto::getId).collect(Collectors.toList());
            if (autoApplyIds != null && autoApplyIds.size() > 0) {
                CommonService.updateApplyInfo(autoApplyIds, -1);
            }
            applyAutoListTmp=null;
            autoApplyIds=null;
            applyAutoList=null;
        }

        //更新关联入库单的关联ID
        workOrderService.update(null, Wrappers.<WorkOrder>update()
                .in("ref_transfer_id", idList)
                .set("ref_transfer_id", null));
        return super.removeCascadeByIds(idList);
    }


    /**
     * 级联删除调拨需求表wm_transfer_demand
     *
     * @param transfer
     */
    private void removeTransferDemand(Transfer transfer) {

        List<TransferDemand> transferDemandList = transferDemandMapper.selectList(Wrappers.<TransferDemand>query().eq("transfer_id", transfer.getId()));
        if (transferDemandList != null && transferDemandList.size() > 0) {
            //更新请领需求明细响应数量
            ApplyItemServiceImpl.updateResponseForTransferDelete(transferDemandList,transfer);

            transferDemandMapper.delete(new QueryWrapper<TransferDemand>().eq("transfer_id", transfer.getId()));
        }
    }

    /**
     * 根据订单自动转换调拨单
     *
     * @param workOrder
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Transfer autoChangeTransfer(WorkOrder workOrder) {
        String deptIdTo = workOrder.getDeptIdTo();
        workOrder = workOrderService.getById(workOrder.getId()); //再请求一遍
        workOrder.setDeptIdTo(deptIdTo);
        String refTransferId = workOrder.getRefTransferId();
        if (refTransferId != null && !"0".equalsIgnoreCase(refTransferId)) {
            throw new BusinessException(String.format("该订单已经转换过调拨单，调拨单号为%s，不能重复转换", refTransferId));
        }
        Transfer transfer = convertTransferByOrder(workOrder);

        //更新因自动请领产生的二级库调拨需求单对应的RefId
        List<ApplyAuto> applyAutos = applyAutoService.list(new QueryWrapper<ApplyAuto>().eq("ref_order_id", workOrder.getPurchaseOrderId()));
        if (applyAutos != null && applyAutos.size() > 0) {
            applyAutos.stream().forEach(applyAuto -> {
                double count = transferItemMapper.selectCount(new QueryWrapper<TransferItem>()
                        .eq("transfer_id", transfer.getId())
                        .eq("matr_id", applyAuto.getMatrId()));
                applyAuto.setRefId(transfer.getId()).setPickedSkuQty(count);
            });

            applyAutoService.saveOrUpdateBatch(applyAutos);
        }
        return transfer;
    }

    /**
     * 根据送货单转换调拨单
     *
     * @param workOrder
     * @returnf
     */
    private Transfer convertTransferByOrder(WorkOrder workOrder) {
        // 查询送货明细
        List<WorkOrderItem> workOrderItems = workOrderItemMapper.selectList(Wrappers.<WorkOrderItem>query().eq("work_order_id", workOrder.getId()));
        if (workOrderItems.isEmpty()) {
            throw new BusinessException("送货明细为空，转换调拨单失败");
        }

        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("line_id", workOrderItems.stream().map(WorkOrderItem::getLineId).collect(Collectors.toList())));
        if (stockList.isEmpty()) {
            throw new BusinessException("入库明细为空，转换调拨单失败");
        }
        boolean isReagent = PurchaseType.REAGENT.getKey().equals(workOrder.getPurchaseOrderType());
        String settlement;

        Order order = orderService.getById(workOrder.getPurchaseOrderId());
//        if (order == null) {
//            throw new BusinessException("采购单为空，转换调拨单失败，请手工申请配送");
//        }
        //申请科室信息
        Dept dept = null;
        if (StringUtils.isNotBlank(workOrder.getDeptIdTo())) {
            dept = deptService.getById(workOrder.getDeptIdTo());
        } else {
            dept = deptService.getById(order.getDeptIdDemand());
        }

        boolean isSafe = false;
        if (order != null && order.getRefTable() != null && "dept_demand".equals(order.getRefTable())) {
            Demand demand = demandService.getById(order.getRefId());
            if (GlobalConsts.AUTO_REF_TABLE_SAFE.equals(demand.getRefTable())) {
                // 安全库存自动请领
                isSafe = true;
            }
        }

        if (order != null && isSafe && StringUtils.isNotEmpty(order.getSettlementType())) {
            //根据采购订单结算类型
            settlement = order.getSettlementType();
        } else if (isReagent) {
            // 试剂根据库存试剂自动转换结算类型
            List<String> matrIds = workOrderItems.stream().map(WorkOrderItem::getMatrId).collect(Collectors.toList());
            List<Matr> matrList = matrMapper.selectBatchIds(matrIds);
            settlement = matrList.stream().anyMatch(Matr::getFlagStockReagent) ? GlobalConsts.SettlementType.usedSettlement : GlobalConsts.SettlementType.outSettlement;
        } else {
            settlement = workOrder.getFlagOmoc() ? GlobalConsts.SettlementType.usedSettlement : GlobalConsts.SettlementType.outSettlement;
        }

        if (dept == null || StringUtils.isBlank(dept.getWarehouseId())) {
            throw new BusinessException(String.format("请至院方科室管理界面为%s配置二级库", dept.getName()));
        }
        BusinessException.throwErrorIf(workOrder.getWarehouseId().equalsIgnoreCase(dept.getWarehouseId()), "调入调出仓库相同，请更换不同的收货科室");

        Transfer transfer = new Transfer();
        transfer.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY))
                .setBusDate(new Date()).setBusUser(UserUtils.currentUser().getName()).setBusUserCode(UserUtils.currentUser().getWorkNo())
                .setFromWarehouseId(workOrder.getWarehouseId()).setFromWarehouseName(workOrder.getWarehouseName())
                .setToWarehouseId(dept.getWarehouseId()).setToWarehouseName(dept.getWarehouseName())
                .setDeptIdTo(dept.getId()).setDeptNameTo(dept.getName())
                .setSettlementType(settlement)
                .setType(GlobalConsts.TransferType.distOut)
                .setReagentTeamId(workOrder.getReagentTeamId()).setPicker(UserUtils.currentUser().getName()).setPickDate(new Date())
                .setRemark(String.format("由入库单%s转换而来", workOrder.getBusKey())).setTransferType((UtilCommon.getStr(workOrder.getPurchaseOrderType(), "matr")))
                .setFlowStatus(GlobalConsts.FLOW_PICKING_VALUE);

        Location toLocation = locationService.getDefaultLocation(dept.getWarehouseId()); //调拨调入默认库位
        if (toLocation != null) {
            transfer.setToLocationId(toLocation.getId()).setToLocationName(toLocation.getName());
        }

        Location fromLocation = locationService.getDefaultLocation(workOrder.getWarehouseId()); //调拨调出默认库位
        if (fromLocation != null) {
            transfer.setFromLocationId(fromLocation.getId()).setFromLocationName(fromLocation.getName());
        }


        baseMapper.insert(transfer);

        transferItemService.addTransferItems(transfer.getId(), stockList.stream().map(Stock::getSn).collect(Collectors.toList()), null);

        //赋值生成的调拨单号
        workOrderService.update(Wrappers.<WorkOrder>update().eq("id",workOrder.getId()).set("ref_transfer_id", transfer.getId()));
        return transfer;
    }

    @Override
    protected void beforeHandleFlowChange(FlowContext flowContext, Transfer newEntity) {
        List<TransferItem> transferItemList = transferItemMapper.selectList(Wrappers.<TransferItem>query().eq("transfer_id", newEntity.getId()));
        this.checkOne(flowContext.getButton(),newEntity,transferItemList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void afterHandleFlowChange(FlowContext flowContext, Transfer newEntity) {
        // 如果是结束节点，创建出库单,入库单和库存表
        List<TransferItem> transferItems = transferItemService.list(Wrappers.<TransferItem>query().eq("transfer_id", newEntity.getId()));
        this.doOne(flowContext,flowContext.getButton(), newEntity, transferItems);
    }

    private void updateApplyInfo(Transfer transfer, List<TransferDemand> transferDemands,FlowContext flowContext) {
        List<String> applyItemIds;
        //是否挂单
        if (ParameterUtils.isApplyHolder()) { //不对虚拟锁做任何操作，未拣货或者未完成拣货的配送明细还原到需求池中   只有请领需求来源的明细需要挂单
            List<TransferDemand> unCompleteDemands = transferDemands.stream()
                    .filter(transferDemand ->
                            transferDemand.getSkuQty() > transferDemand.getPickedSkuQty()
                                    && GlobalConsts.AUTO_REF_TABLE_APPLY.equalsIgnoreCase(transferDemand.getRefDemandTable()))
                    .collect(Collectors.toList());
            if (unCompleteDemands != null && unCompleteDemands.size() > 0) {
                List<String> autoApplyIds = unCompleteDemands.stream().map(TransferDemand::getApplyAutoId).collect(Collectors.toList());
//            需要挂单的需求拣货情况
                List<TransferDemand> autoTransferList = transferDemandMapper.selectList(Wrappers.<TransferDemand>query().in("apply_auto_id", autoApplyIds));
                Map<String, List<TransferDemand>> autoIdMap = autoTransferList.stream().collect(Collectors.groupingBy(TransferDemand::getApplyAutoId));
                unCompleteDemands.forEach(demand -> {
                    List<String> msgArr = new ArrayList<>();
                    List<TransferDemand> transferDemandsPicks = autoIdMap.get(demand.getApplyAutoId());
                    double pickQty = 0D;
                    for (TransferDemand item : transferDemandsPicks) {
                        pickQty += item.getPickedSkuQty();
                        msgArr.add(UtilNum.trimZero((item.getPickedSkuQty() / item.getBillPackageRate())) + item.getBillPackageUnit());

                    }

                    applyAutoService.update(Wrappers.<ApplyAuto>update().set("ref_id", null)
                            .set("picked_sku_qty", pickQty)
                            .set("sku_qty_apply", demand.getSkuQty() - demand.getPickedSkuQty())
                            .set("demand_package_qty", (demand.getSkuQty() - demand.getPickedSkuQty()) / demand.getPackageRate())
                            .set("pick_msg", StringUtils.join(msgArr, "\n"))
                            .set("bill_package_qty", 0D)
                            .set("gmt_update", new Date())
                            .eq("id", demand.getApplyAutoId()));
                    msgArr.clear();
                });
            }
            applyItemIds = transferDemands.stream().filter(demand -> demand.getBillSkuQty().equals(demand.getPickedSkuQty())  && StringUtils.isNotBlank(demand.getRefApplyItemId())).map(TransferDemand::getRefApplyItemId).collect(Collectors.toList());
        } else { //释放所有的虚拟锁
            applyItemIds = transferDemands.stream().filter(demand -> StringUtils.isNotBlank(demand.getRefApplyItemId())).map(TransferDemand::getRefApplyItemId).collect(Collectors.toList());
        }
        if (!applyItemIds.isEmpty()) {
            stockLockService.virtualUnlockList(LockBusType.APPLY, applyItemIds, transfer.getFromWarehouseId());
        }

        List<ApplyAuto> applyAutoList = applyAutoService.list(new QueryWrapper<ApplyAuto>().eq("ref_id", transfer.getId()));
        if (applyAutoList.size() > 0) {
            applyAutoList.forEach(item -> {
                if (StringUtils.isBlank(item.getRefOrderId())) { //不是自动请领产生的
                    ApplyAutoHistory applyAutoHistory = new ApplyAutoHistory();
                    BeanUtils.copyProperties(item, applyAutoHistory);
                    applyAutoHistory.setId(null);
                    applyAutoHistory.setGmtComplete(LocalDateTime.now()).setStatus("1");
                    applyAutoHistoryService.save(applyAutoHistory);
                    applyAutoService.removeById(item);
                }
            });
        }

        //更新请领需求明细响应状态
        if (!applyItemIds.isEmpty()) {
            ApplyItemServiceImpl.updateResponseForTransferFlowChange(flowContext.getButton().getValueTo(), flowContext.getFlowStatusTextTo(), applyItemIds, transfer);
        }
    }

    /**
     * @param isUnlock true:解锁
     * @Description：调拨单进行出入库 【智能柜专用】
     */
    @Override
    synchronized public void autoOutIn(Transfer transfer, boolean isUnlock) {
        if (GlobalConsts.TransferType.returnIn.equalsIgnoreCase(transfer.getType()) && GlobalConsts.SettlementType.outSettlement.equalsIgnoreCase(transfer.getSettlementType())) { //如果是退库，且出库结算的，不产生出入库
            return;
        }
        //1、创建出库单主表
        List<WorkOrder> outWorkOrderList = outOrderService.createWorkOrderByTransfer(transfer);
        List<WorkOrder> workOrderListAll = new ArrayList<WorkOrder>();
        if (outWorkOrderList == null || outWorkOrderList.size() == 0) {
            throw new BusinessException("转换出库单失败");
        }
        workOrderListAll.addAll(outWorkOrderList);
        //2、创建入库单单明细表
        outWorkOrderList.forEach(item -> {
            WorkOrder inWorkOrder = inOrderService.createWorkOrderByOut(item, transfer);
            workOrderService.post(inWorkOrder.getId(), item.getId(), true);
            workOrderListAll.add(inWorkOrder);
        });

        if (isUnlock) {
            //3、解锁调拨单占用库存
            List<String> stockIds = transferItemMapper.selectList(new QueryWrapper<TransferItem>()
                    .in("transfer_id", transfer.getId()))
                    .stream().map(TransferItem::getStockId).collect(Collectors.toList());
            if (stockIds != null && stockIds.size() > 0) {
                stockIds.forEach(id -> stockLockService.unlock(id));
            }
        }
        log.info("调拨共生成{}个入库单", outWorkOrderList.size());


        //更新耗材库存仓库关系
        CommonService.updateMatrStockNums(workOrderListAll);
    }

    /**
     * @param transfer 实体
     * @Description：调拨单进行出入库 【智能柜专用】，只更改库存仓库库
     */
    @Override
    synchronized public void autoOutInForFsd(Transfer transfer) {
        List<TransferItem> transferItemList = transferItemService.list(Wrappers.<TransferItem>query().eq("transfer_id", transfer.getId()));
        List<String> stockIds = null;
        if (transferItemList != null && transferItemList.size() > 0) {
            stockIds = transferItemList.stream().map(TransferItem::getStockId).collect(Collectors.toList());
        }

        if (!transfer.getFromWarehouseId().equals(transfer.getToWarehouseId())) { //不同物理仓库
            this.autoOutIn(transfer, false);
        } else {  //同一物理仓库
            if (stockIds != null) {
                //1、修改库存
                UpdateWrapper updateWrapper = Wrappers.update();
                updateWrapper.in("id", stockIds);
                updateWrapper.set("location_id", transfer.getToLocationId());
                updateWrapper.set("location_name", transfer.getToLocationName());
                stockService.update(updateWrapper);
            }
        }

        //9、解锁调拨单占用库存
        if (stockIds != null) {
            stockLockService.remove(Wrappers.<StockLock>query().in("stock_id", stockIds));
        }

    }

    @Override
    public List<Map> findAllList(String busKey) {
        QueryWrapper<Transfer> wrapper = new QueryWrapper<>();
//        wrapper.nested(i -> i.eq("flow_status", "start").or().eq("flow_status", "picking"))
        wrapper.nested(i -> i.eq("flow_status", "picking"))
                .and(o -> o.eq("picker", UserUtils.currentUser().getName()).or().isNull("picker"));
        if (StringUtils.isNotBlank(busKey)) {
            wrapper.eq("bus_key", busKey);
        }
        wrapper.orderByDesc("gmt_create");
        List<Map> list = this.baseMapper.findAllList(wrapper);
        if (StringUtils.isNotBlank(busKey) && (list == null || list.size() == 0)) {
            throw new BusinessException("调拨单【%s】已被移除或者不可操作", busKey);
        }
        return list;
    }

    @Override
    public Boolean pickFinishedPDA(String id) {
        if (transferItemMapper.selectCount(Wrappers.<TransferItem>query().eq("transfer_id", id)) < 1)
            throw new BusinessException("该调拨单没有调拨明细，请删除主单据或者添加拣货明细");
        Transfer transfer = new Transfer();
        transfer.setFlowStatus(GlobalConsts.FLOW_PICKED_VALUE).setId(id);
        transfer.setPicker(UserUtils.currentUser().getName()).setPickDate(new Date());
        return this.updateById(transfer);
    }

    @Override
    public String adjustLocation(AdjustLocationDTO adjustLocationDTO) {
        if (adjustLocationDTO.getStocks() == null || adjustLocationDTO.getStocks().size() == 0) {
            throw new BusinessException("传入参数有误，没有调整单的库存信息");
        }
        Map<String, List<Stock>> groupItem = Maps.newHashMap();
        //是否进入盘点模式
        if (ParameterUtils.getFlagStockCheck()) {
            //有未完成的盘点单则全部锁定
            List<StockCheck> stockChecks = stockCheckMapper.selectList(Wrappers.<StockCheck>query().eq("flag_finished", false));
            if (stockChecks != null && stockChecks.size() > 0) {
                throw new BusinessException("当前处于盘点模式，请在盘点结束后再进行操作！");
            }
        }
        //按库位Id进行分类
        adjustLocationDTO.getStocks().stream().filter(item -> StringUtils.isNotBlank(item.getLocationId()) && !adjustLocationDTO.getLocation().getId().equals(item.getLocationId())).forEach(item -> {
            if (groupItem.get(item.getLocationId()) == null) {
                List<Stock> itemList = new ArrayList<>();
                itemList.add(item);
                groupItem.put(item.getLocationId(), itemList);
            } else {
                groupItem.get(item.getLocationId()).add(item);
            }
        });
        int stockChangeNum = 0;
        if (groupItem.size() > 0) {
            Warehouse toWarehouse = warehouseService.getById(adjustLocationDTO.getLocation().getWarehouseId());
            if (toWarehouse == null) {
                throw new BusinessException("传入参数有误，扫码的库位信息有误");
            }
            for (String locationId : groupItem.keySet()) {
                stockChangeNum += groupItem.get(locationId).size();
                addTransferByAdjust(groupItem.get(locationId), adjustLocationDTO.getLocation(), toWarehouse, false);
            }
        }

        return "库存调整成功,数量：" + stockChangeNum;
    }

    @Override
    /**
     * @Description： 库位调整按类型
     */
    @Transactional(rollbackFor = Exception.class)
    public String adjustLocationByType(AdjustLocationDTO adjustLocationDTO) {
        if (StringUtils.isBlank(adjustLocationDTO.getType()) || !Arrays.asList("matr", "stock").contains(adjustLocationDTO.getType())) {
            throw new BusinessException("缺少参数【type】，请刷新重试或者联系开发人员");
        }
        Location location = adjustLocationDTO.getLocation();
        if (location == null) {
            throw new BusinessException("缺少参数【location】，请刷新重试或者联系开发人员");
        }
        if (StringUtils.isBlank(location.getWarehouseId())) {
            throw new BusinessException("库位没有所属仓库，请联系运营人员");
        }

        if (adjustLocationDTO.getType().equals("matr")) {  //该耗材所有库存，未锁定的
            List<Stock> stockList = stockService.list(Wrappers.<Stock>query()
                    .eq("warehouse_id", location.getWarehouseId())
                    .eq("matr_id", adjustLocationDTO.getStocks().get(0).getMatrId())
                    .notInSql("id", "select stock_id from wm_stock_lock  where stock_id is not null")
            );
            if (stockList == null || stockList.size() < 0) throw new BusinessException("当前仓库并没有该耗材的库存，或者相关库存都已被所锁定");
            adjustLocationDTO.setStocks(stockList);
        }
        String msg = this.adjustLocation(adjustLocationDTO);


        int count = stockService.count(Wrappers.<Stock>query()
                .eq("warehouse_id", location.getWarehouseId())
                .eq("matr_id", adjustLocationDTO.getStocks().get(0).getMatrId())
                .notInSql("id", "select stock_id from wm_stock_lock  where stock_id is not null").ne("location_id", location.getId())
        );
        //所有库存都在目标库位，则绑定库位耗材绑定关系
        if (count == 0) {
            //绑定库位耗材关系
            locationLimitMatrService.bindOrUnbind(adjustLocationDTO.getStocks().get(0).getSn(), location.getCode());
            msg += "<br>所有库存都已调整，库位耗材关系已绑定当前库位";
        }
        return msg;
    }

    /**
     * 调整货位
     *
     * @param stocks
     * @param location
     */
    private void addTransferByAdjust(List<Stock> stocks, Location location, Warehouse toWarehouse, boolean isUnlock) {
        //根据第一条数据创建Transfer
        Transfer transfer = new Transfer();
        Stock stock = stocks.get(0);
        Double amount = stocks.stream().mapToDouble(Stock::getAmount).sum();
        transfer.setFromWarehouseId(stock.getWarehouseId())
                .setFromWarehouseName(stock.getWarehouseName())
                .setToWarehouseId(toWarehouse.getId())
                .setToWarehouseName(toWarehouse.getName())
                .setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY))
                .setBusDate(Calendar.getInstance().getTime())
                .setBusUser(UserUtils.currentUser().getName())
                .setBusUserCode(UserUtils.currentUser().getWorkNo())
                .setFromLocationId(stock.getLocationId())
                .setFromLocationName(stock.getLocationName())
                .setAmount(amount)
                .setRemark(String.format("APP端库位调整创建"))
                .setFlowStatus("end")
                .setFlowModifier(UserUtils.currentUser().getName())
                .setFlowModified(LocalDateTime.now());
        if (location != null) {
            transfer.setToLocationId(location.getId())
                    .setToLocationName(location.getName());
        }
        super.save(transfer);

        List<TransferItem> transferItems = Lists.newArrayListWithCapacity(stocks.size());

        stocks.forEach(stock1 -> {
            TransferItem transferItem = new TransferItem();
            BeanUtils.copyProperties(stock1, transferItem);
            transferItem.setTransferId(transfer.getId())
                    .setStockId(stock1.getId())
                    .setFromLocationId(stock.getLocationId())
                    .setFromLocationName(stock.getLocationName())
                    .setId(null)
                    .setGmtCreate(LocalDateTime.now())
                    .setCreator(UserUtils.currentUser().getName())
                    .setModifier(null)
                    .setGmtCreate(null);
            if (location != null) {
                transferItem.setToLocationId(location.getId())
                        .setToLocationName(location.getName());
            }
            transferItems.add(transferItem);
        });
        transferItemService.saveBatch(transferItems);
        autoOutIn(transfer, isUnlock);
    }

    /**
     * 开单科室与库存地科室不同时生成调拨单过账
     *
     * @param
     * @return
     */
    private void convertTransferByStock(WorkOrder workOrder) {

    }

    @Override
    public TransferVO findByIdForPDA(String key, String value) {
        TransferVO transferVO = this.getBaseMapper().findByIdForPDA(key, value);
        if (transferVO != null) {

            Double TransferDemandSkuQty = transferDemandMapper.selectList(Wrappers.<TransferDemand>query().eq("transfer_id", transferVO.getId()).select("sku_qty"))
                    .stream().filter(transferDemand -> transferDemand.getSkuQty() != null).mapToDouble(TransferDemand::getSkuQty).sum();

            Double TransferItemSkuQty = transferItemMapper.selectList(Wrappers.<TransferItem>query().eq("transfer_id", transferVO.getId()).select("sku_qty"))
                    .stream().filter(transferDemand -> transferDemand.getSkuQty() != null).mapToDouble(TransferItem::getSkuQty).sum();
            transferVO.setPickAndDemand(UtilNum.getString(TransferDemandSkuQty) + "|" + UtilNum.getString(TransferItemSkuQty));
        }
        return transferVO;
    }

    @Override
    public Map<String, Object> getByBusKey(String busKey) {
        Transfer transfer = this.getOne(Wrappers.<Transfer>query().eq("bus_key", busKey));
        if (transfer == null) {
            return null;
        }
        List<TransferItem> transferItems = transferItemService.list(Wrappers.<TransferItem>query().eq("transfer_id", transfer.getId()));

        Map<String, Object> map = Maps.newHashMap();
        map.put("transfer", transfer);
        map.put("transferItems", transferItems);
        return map;
    }

    @Override
    public void beforeHandleFlowChangeBatch(FlowContext flowContext, List<Transfer> entityList,List<String> busIdList) {
    }

    /**
    * @Description： 批量审核，增量操作
    */
    @Override
    public void afterHandleFlowChangeBatch(FlowContext flowContext, List<Transfer> entityList,List<String> busIdList) {
        List<TransferItem> transferItems = transferItemService.list(Wrappers.<TransferItem>query().in("transfer_id", busIdList));
        Map<String,List<TransferItem>> transferItemMap = transferItems.stream().collect(Collectors.groupingBy(TransferItem::getTransferId));

        entityList.stream().forEach(transfer -> {
            TransactionStatus transactionStatus = null;
            try {
                this.checkOne(flowContext.getButton(),transfer,transferItemMap.get(transfer.getId()));
                transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
                this.doOne(flowContext,flowContext.getButton(),transfer, transferItemMap.get(transfer.getId()));
                dataSourceTransactionManager.commit(transactionStatus);
            }catch (Exception e) {
                log.error("afterHandleFlowChangeBatch.error",e);

                //失败列表
                flowContext.addBusIdError(transfer.getId(),transfer.getBusKey(),e.getMessage());
                if (transactionStatus != null) {
                    dataSourceTransactionManager.rollback(transactionStatus);
                }
            }
        });
    }

    /**
     * 调拨明细为空时的提示信息
     *
     * @param buttonCode
     * @param transferType
     * @return null: 不需要做判断
     */
    private String msgIfItemEmpty(String buttonCode, String transferType) {
        if ("submit".equalsIgnoreCase(buttonCode) && GlobalConsts.TransferType.returnIn.equalsIgnoreCase(transferType)) { //提交退库
            return "没有退库明细，不能退库！";
        }
        if ("end_pick".equalsIgnoreCase(buttonCode) && GlobalConsts.TransferType.distOut.equalsIgnoreCase(transferType)) { //拣货完成
            return "没有拣货明细，不能完成拣货！";
        }
        return null;
    }

    //校验单个调拨单
    protected void checkOne(FlowButton flowButton, Transfer newEntity,List<TransferItem> transferItemList) {
        //需要校验调拨明细为空的
        String strEmpty = msgIfItemEmpty(flowButton.getCode(), newEntity.getType());
        if (strEmpty != null && (transferItemList == null || transferItemList.size() < 1)) {
            BusinessException.throwValidFail(strEmpty);
        }

        if (transferItemList != null && transferItemList.size() > 0){
            //是否禁用耗材校验
            matrPassUtil.ckeckMatrPass("transfer", transferItemList.stream().map(TransferItem::getMatrId).collect(Collectors.toList()));

            //预算检查
            UtilBudget.checkBudgetControl(deptService.getDeptByWarehouseId(newEntity.getToWarehouseId(), newEntity.getToWarehouseName()).getId(), transferItemList);
        }

        switch (flowButton.getCode()) {
            case "submit": //提交
                //自动科室需求、手工科室需求需要包含需求明细
                if (GlobalConsts.DistTransferSourceType.DEPT_APPLY_AUTO.equals(newEntity.getSourceType()) || GlobalConsts.DistTransferSourceType.DEPT_APPLY.equals(newEntity.getSourceType())) {
                    int itemSize = transferDemandMapper.selectCount(Wrappers.<TransferDemand>query().eq("transfer_id", newEntity.getId()));
                    if (itemSize < 1) {
                        BusinessException.throwValidFail("当前单据需要先手动创建需求，请添加好需求再提交");
                    }
                }
                if (StringUtils.isNotBlank(newEntity.getPicker())) { //可能是取消拣货，此时删除拣货人
                    this.update(Wrappers.<Transfer>update().set("picker", null).eq("id", newEntity.getId()));
                }
                break;
            case "picking": //开始拣货，如果没有拣货人，获取当前操作人员
                if (StringUtils.isBlank(newEntity.getPicker())) {
                    newEntity.setPicker(UserUtils.currentUser().getName());
                    this.updateById(newEntity);
                }
                break;
            default:
                break;
        }
    }


    //数量化管理 科室退货入库
    public void minusStockMatrNumReturn(Transfer transfer, List<TransferItem> transferItemList) {
        List<String> snList = transferItemList.stream().map(TransferItem::getSn).collect(Collectors.toList());
        //批量解除实物锁
        //stockLockService.unlockBatchWithBusKeyValue(snList,transfer.getId());

        //扣减库存
        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("sn", snList));
//        stockService.listStockWithZero()
        Map<String,TransferItem> transferItemMapBySn = transferItemList.stream().collect(Collectors.toMap(TransferItem::getSn, t -> t));
        stockList.forEach(stock -> {
            double skuQtyNew = stock.getSkuQty() + transferItemMapBySn.get(stock.getSn()).getSkuQty();
            stock.setSkuQty(skuQtyNew);
            stock.setPackageQty(stock.getSkuQty());
            stock.setAmount(stock.getSkuPrice() * stock.getSkuQty());
//            stock.setWarehouseId(transfer.getFromWarehouseId());
//            stock.setWarehouseName(transfer.getFromWarehouseName());
//            stock.setLocationId(transfer.getFromLocationId());
//            stock.setLocationName(transfer.getFromLocationName());
        });
        stockService.updateBatchById(stockList);
    }


    //数量化管理 扣减库存
    public void minusStockMatrNum(Transfer transfer, List<TransferItem> transferItemList) {
        List<String> snList = transferItemList.stream().map(TransferItem::getSn).collect(Collectors.toList());
        //批量解除实物锁
        stockLockService.unlockBatchWithBusKeyValue(snList,transfer.getId());

        //扣减库存
        List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("sn", snList));
        Map<String,TransferItem> transferItemMapBySn = transferItemList.stream().collect(Collectors.toMap(TransferItem::getSn, t -> t));
        stockList.forEach(stock -> {
            double skuQtyNew = stock.getSkuQty() - transferItemMapBySn.get(stock.getSn()).getSkuQty();
            if (skuQtyNew  < 0) {
                throw  new BusinessException("库存不足");
            }
            stock.setSkuQty(skuQtyNew);
            stock.setPackageQty(stock.getSkuQty());
            stock.setAmount(stock.getSkuPrice() * stock.getSkuQty());
            stock.setWarehouseId(transfer.getFromWarehouseId());
            stock.setWarehouseName(transfer.getFromWarehouseName());
            stock.setLocationId(transfer.getFromLocationId());
            stock.setLocationName(transfer.getFromLocationName());
        });
        stockService.updateBatchById(stockList);
    }

    @Override
    public void doOne(FlowContext flowContext, FlowButton flowButton, Transfer transfer, List<TransferItem> transferItemList) {
        switch (flowButton.getCode()) {
            case "cancelPicking": {//取消拣货
                transferItemService.cancelTransferPick(transfer.getId());
                break;
            }
        }

        //响应配送需求的调拨需求
        List<TransferDemand> transferDemandsFromApplyAuto = transferDemandMapper.selectList(Wrappers.<TransferDemand>query().eq("transfer_id", transfer.getId()).isNotNull("apply_auto_id"));

        if (!flowButton.getFlagEnd()){
            List<String> applyItemIds = transferDemandsFromApplyAuto.stream().filter(auto -> StringUtils.isNotBlank(auto.getRefApplyItemId())).map(TransferDemand::getRefApplyItemId).collect(Collectors.toList());
            if (applyItemIds != null && applyItemIds.size() > 0) {
                if (flowButton.getCode().equals("cancelPicking")) {  //取消拣货特殊处理
                    //更新请领需求明细响应状态
                    ApplyItemServiceImpl.updateResponseForTransferFlowChange(GlobalConsts.FLOW_START_VALUE,"制单", applyItemIds, transfer);
                }else {
                    //更新请领需求明细响应状态
                    ApplyItemServiceImpl.updateResponseForTransferFlowChange(flowContext.getButton().getValueTo(),flowContext.getFlowStatusTextTo(), applyItemIds, transfer
                    );
                }
            }
            return;
        }
        //审核通过效期校验
        if (GlobalConsts.TransferType.distOut.equalsIgnoreCase(transfer.getType())) {
            Warehouse warehouse = warehouseService.getById(transfer.getToWarehouseId());
            if (warehouse == null) {
                throw new BusinessException("调入仓库在系统中不存在,请检查！");
            }
            if (!warehouse.getFlagCenter()) {//调入仓库为非中心仓库，进行效期验证
                stockService.getExpStockByUC(transferItemList.stream().map(TransferItem::getSn).collect(Collectors.toList()));
            }
        }

        Warehouse fromWarehouse = warehouseService.getById(transfer.getFromWarehouseId());
        Warehouse toWarehouse = warehouseService.getById(transfer.getToWarehouseId());
        if (!fromWarehouse.getFlagCenter() && !toWarehouse.getFlagCenter()) {//调入和调出仓库都不是中心仓库时
            Warehouse fromApplyWarehouse = warehouseService.getById(fromWarehouse.getApplyWarehouseId());
            if (!fromApplyWarehouse.getFlagCenter()) {//该请领仓库依然不是中心仓库
                // todo 多中心仓库有问题，三级库也会出现问题，暂时不处理
                throw new BusinessException("三级库和二级库之间的调拨暂不支持！");
            } else {
                List<String> stockIds = transferItemList.stream().map(TransferItem::getStockId).collect(Collectors.toList());
                List<Stock> stocks = stockService.listStockWithZero(Wrappers.<Stock>query().in("id", stockIds), null);
                Location location = locationService.getDefaultLocation(fromApplyWarehouse.getId());
                this.addTransferByAdjust(stocks, location, fromApplyWarehouse, false);
                stocks = stockService.listStockWithZero(Wrappers.<Stock>query().in("id", stockIds), null);//耗材库位会变化，需要重新查一下
                Location location2 = locationService.getDefaultLocation(toWarehouse.getId());
                this.addTransferByAdjust(stocks, location2, toWarehouse, true);
            }
        } else {
            //调拨单进行出入库
            autoOutIn(transfer, GlobalConsts.SettlementType.usedSettlement.equalsIgnoreCase(transfer.getSettlementType()));

            //创建科室消耗单
            if (GlobalConsts.SettlementType.outSettlement.equalsIgnoreCase(transfer.getSettlementType())) { //出库结算的，立即出库并生成科室消耗单
                useService.createByTransfer(transfer, transferItemList);

                // 数量化管理的耗材id
                List<String> matrIdListMagnum = matrMapper.selectBatchIds(transferItemList.stream().map(TransferItem::getMatrId).collect(Collectors.toList()))
                        .stream().filter(item -> item.getFlagMagnum() != null && item.getFlagMagnum()).map(Matr::getId).collect(Collectors.toList());


                //出库维护
                if ( GlobalConsts.TransferType.distOut.equalsIgnoreCase(transfer.getType())) {
                    // 因为是调拨加锁的，此处根据调拨明细进行扣减库存
                    if (matrIdListMagnum.size() > 0) {
                        minusStockMatrNum(transfer,transferItemList.stream().filter(tmp -> matrIdListMagnum.contains(tmp.getMatrId())).collect(Collectors.toList()));
                    }
                }

                Location location = locationService.getDefaultLocation(transfer.getToWarehouseId());
                List<String> stockIds = transferItemList.stream().map(TransferItem::getStockId).collect(Collectors.toList());
                List<Stock> stocks = stockService.listStockWithZero(Wrappers.<Stock>query().in("id", stockIds), null);
                List<String> packStockIds = stocks.stream().filter(stock -> !"0".equals(stock.getPackStockId())).map(Stock::getPackStockId).distinct().collect(Collectors.toList());
                if (GlobalConsts.TransferType.returnIn.equalsIgnoreCase(transfer.getType())) {//如果是退库的，直接推到中心仓库去
                    if (!packStockIds.isEmpty()) {
                        stockService.update(Wrappers.<Stock>update().in("id", packStockIds)
                                .set("warehouse_id", transfer.getToWarehouseId()).set("warehouse_name", transfer.getToWarehouseName())
                                .set("location_id", location.getId()).set("location_name", location.getName()));
                    }
                    //1、创建出库单主表
                    List<WorkOrder> outWorkOrderList = outOrderService.createWorkOrderByTransfer(transfer);
                    if (outWorkOrderList.isEmpty()) {
                        throw new BusinessException("转换出库单失败");
                    }
                    //2、创建入库单单明细表
                    outWorkOrderList.forEach(item -> {
                        WorkOrder inWorkOrder = inOrderService.createWorkOrderByOut(item, transfer);
                        workOrderService.post(inWorkOrder.getId(), item.getId(), false);
                    });

                    // 增加库存
                    if (matrIdListMagnum.size() > 0) {
                        //minusStockMatrNumReturn(transfer,transferItemList.stream().filter(tmp -> matrIdListMagnum.contains(tmp.getMatrId())).collect(Collectors.toList()));
                    }
                } else { //出库，修正pack包的数量为0
                    if (!packStockIds.isEmpty()) {
                        List<Stock> packStocks = stockService.list(Wrappers.<Stock>query().in("id", packStockIds));
                        if (!packStockIds.isEmpty()) {
                            List<StockZero> stockZeros = Lists.newArrayListWithCapacity(packStockIds.size());
                            stockService.removeByIds(packStockIds);
                            for (Stock stock : packStocks) {
                                stock.setPackageQty(0.0);
                                stock.setSkuQty(0.0);
                                stock.setAmount(0.0);
                                stock.setMdate(new Date());

                                StockZero stockZero = new StockZero();
                                BeanUtils.copyProperties(stock, stockZero);
                                stockZeros.add(stockZero);
                            }
                            stockZeroService.saveBatch(stockZeros);
                        }
                    }
                }
            }
        }

        //更新请领相关的数量和状态
        if (transferDemandsFromApplyAuto != null && transferDemandsFromApplyAuto.size() > 0) {
            updateApplyInfo(transfer, transferDemandsFromApplyAuto,flowContext);
        }
    }

}
